import copy
import io
import json
import logging

import numpy as np
from PIL import Image
from nanoid import generate
from nicegui import ui, events
from nicegui.events import ValueChangeEventArguments
from starlette.formparsers import MultiPartParser

from DefaultParams import DefaultParams
from DefaultParamsCtl import DefaultParamsCtl
from GaussBlurParams import GaussBlurParams
from GaussBlurParamsCtl import GaussBlurParamsCtl
from MedianBlurParams import MedianBlurParams
from MedianBlurParamsCtl import MedianBlurParamsCtl
from ThresholdParams import ThresholdParams
from ThresholdParamsCtl import ThresholdParamsCtl
from opencv_algo_wrapper import OpencvAlgoWrapper

MultiPartParser.max_file_size = 1024 * 1024 * 10  # 5 MB


class OpencvCalibrationFrame:
    def __init__(self, logger: logging.Logger):
        self.json_editor = None
        self.selected_algo_alias = None
        self.selected_algo_ui = None
        self.json_selected_algo_params = None
        self.src_img_pil = None
        self.src_img_opencv = None
        self.logger = logger
        self.opencv_algo_wrapper = OpencvAlgoWrapper(logger)
        self.added_algo_item_list = []
        self.applied_algo_item_list = []
        self._build()

    def generate_nanoid(self):
        return generate(alphabet='abcdef1234567890', size=5)

    def handle_mouse_iimage_init(self, e: events.MouseEventArguments):
        color = 'SkyBlue' if e.type == 'click' else 'SteelBlue'
        self.src_iimage.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="1" fill="none" stroke="{color}" stroke-width="4" />'
        ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')

    def handle_mouse_ii_processed(self, e: events.MouseEventArguments):
        color = 'SkyBlue' if e.type == 'click' else 'SteelBlue'
        self.dst_img_processed.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="1" fill="none" stroke="{color}" stroke-width="4" />'
        ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')

    def handle_fileuploader_pic_upload(self, e: events.UploadEventArguments):
        self.logger.debug('handle PIC upload')
        src_img_file_data = e.content.read()

        # 使用BytesIO将字节数据转为文件对象
        src_img_bytes = io.BytesIO(src_img_file_data)

        # img_cv2 = cv2.imdecode(np.asarray(bytearray(bytes_data), dtype='uint8'), cv2.IMREAD_COLOR)

        # 使用PIL的Image类从文件对象中读取图像
        self.src_img_pil = Image.open(src_img_bytes)

        # 如果需要转换为OpenCV的图像格式
        self.src_img_opencv = np.array(self.src_img_pil)

        self.logger.debug(f'uploaded file length={len(src_img_file_data)}')

        self.src_iimage.set_source(self.src_img_pil)
        self.src_iimage.update()
        # content.set_content(text)
        # dialog.open()

    def handle_selector_algo_selected(self, e: ValueChangeEventArguments):
        self.selected_algo_alias = e.value

    def callback_update_aggrid(self, algo_id: str, json_algo_params):
        for algo_item in self.added_algo_item_list:
            if algo_item['algo_id'] == algo_id:
                algo_item['algo_params'] = json_algo_params

        self.algo_aggrid.update()

    def do_image_processing(self):
        self.logger.debug(f'selected algo list={self.ui_select_algo_list.value}')
        if len(self.ui_select_algo_list.value) < 1:
            self.dst_img_processed.set_source(self.src_img_pil)
        else:
            img = copy.copy(self.src_img_opencv)
            img_pil = Image.fromarray(self.src_img_opencv)
            for algo in list(self.ui_select_algo_list.value):
                if algo == 'openning':
                    img, img_pil = self.opencv_algo_wrapper.opencv_openning(img, (5, 5))
                elif algo == 'grey':
                    img, img_pil = self.opencv_algo_wrapper.opencv_grey(img)
                elif algo == 'gaussblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_guass_blur(img)
                elif algo == 'medianblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_median_blur(img)
                elif algo == 'sobelblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_sobel_blur(img)
                elif algo == 'cannyblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_canny_blur(img)
                elif algo == 'threshold':
                    img, img_pil = self.opencv_algo_wrapper.opencv_threshold(img, 150, 255)
                elif algo == 'contours':
                    img, img_pil = self.opencv_algo_wrapper.opencv_find_contours(img)
                elif algo == 'erode':
                    img, img_pil = self.opencv_algo_wrapper.opencv_erode(img)
                else:
                    pass

            self.logger.debug(f'the type of processed image={type(img)}')
            self.dst_img_processed.set_source(img_pil)

    def handle_btn_add_to_algo_list(self):
        self.logger.debug(f'add the selected algo to aggrid: {self.selected_algo_alias}')
        default_algo_item = self.opencv_algo_wrapper.get_default_algo_item_by_alias(self.selected_algo_alias)
        new_algo_id = self.generate_nanoid()
        algo_item = {"algo_id": new_algo_id,
                     "algo_alias": self.selected_algo_alias,
                     "algo_name": default_algo_item['algo_name'],
                     "algo_params": json.dumps(default_algo_item['algo_params'])}
        self.added_algo_item_list.append(algo_item)
        self.algo_aggrid.update()

    async def handle_btn_remove_from_algo_list(self):
        selected_rows = await self.algo_aggrid.get_selected_rows()
        self.added_algo_item_list[:] = [row for row in self.added_algo_item_list if row not in selected_rows]
        self.algo_aggrid.update()

    async def handle_btn_apply_algo_of_list(self):
        self.applied_algo_item_list = await self.algo_aggrid.get_selected_rows()
        self.logger.debug(f'selected algo list={self.applied_algo_item_list}')
        if len(self.applied_algo_item_list) < 1:
            self.dst_img_processed.set_source(self.src_img_pil)
            return

        img = copy.copy(self.src_img_opencv)
        img_pil = Image.fromarray(self.src_img_opencv)

        for algo in self.applied_algo_item_list:
            if algo['algo_id'] == 'openning':
                img, img_pil = self.opencv_algo_wrapper.opencv_openning(img, (5, 5))
            elif algo['algo_id'] == 'grey':
                img, img_pil = self.opencv_algo_wrapper.opencv_grey(img)
            elif algo['algo_id'] == 'gaussblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_guass_blur(img)
            elif algo['algo_id'] == 'medianblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_median_blur(img)
            elif algo['algo_id'] == 'sobelblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_sobel_blur(img)
            elif algo['algo_id'] == 'cannyblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_canny_blur(img)
            elif algo['algo_id'] == 'threshold':
                img, img_pil = self.opencv_algo_wrapper.opencv_threshold(img, 150, 255)
            elif algo['algo_id'] == 'contours':
                img, img_pil = self.opencv_algo_wrapper.opencv_find_contours(img)
            elif algo['algo_id'] == 'erode':
                img, img_pil = self.opencv_algo_wrapper.opencv_erode(img)
            else:
                pass

        self.logger.debug(f'the type of processed image={type(img)}')
        self.dst_img_processed.set_source(img_pil)

    @ui.refreshable
    def build_algo_params_panel(self):
        ui.notify(f'selected_row_data={self.json_selected_algo_params}')
        if self.selected_algo_ui is None or self.json_selected_algo_params is None:
            pass
        else:
            with ui.card().classes('w-full'):
                self.selected_algo_ui.build_ui(self.json_selected_algo_params)

    def process_selected_algo(self, algo_item):
        self.logger.debug(f'selected algo={algo_item}')

        str_params = algo_item['algo_params']

        if algo_item['algo_alias'] == 'default':
            section_ui = DefaultParamsCtl(self.logger)
        elif algo_item['algo_alias'] == 'gauss_blur':
            section_ui = GaussBlurParamsCtl(self.logger)
        elif algo_item['algo_alias'] == 'median_blur':
            section_ui = MedianBlurParamsCtl(self.logger)
        elif algo_item['algo_alias'] == 'threshold':
            section_ui = ThresholdParamsCtl(self.logger, algo_item['algo_id'], self.callback_update_aggrid)
        else:
            section_ui = DefaultParamsCtl(self.logger)
            params = DefaultParams()
            params.algo_alias = algo_item['algo_alias']
            params.algo_name = algo_item['algo_name']
            params.code = 1000
            params.errmsg = 'Not supported yet.'
            str_params = DefaultParams.model_dump_json(params)
            self.logger.error('error: select algo NOT supported yet.')

        self.selected_algo_ui = section_ui
        self.json_selected_algo_params = str_params

        self.build_algo_params_panel.refresh()

    def handle_row_selected(self, event):
        selected_algo = event.args['data']
        ui.notify(f'selected_algo={selected_algo}')
        if selected_algo is not None:
            self.process_selected_algo(selected_algo)

    def convert_algo_item(self, algo_item):
        str_algo_params = algo_item['algo_params']
        algo_item['algo_params'] = json.loads(str_algo_params)
        return algo_item

    def export_algo_list(self):
        converted_algo_list = list(map(self.convert_algo_item, self.added_algo_item_list))
        filename = ''
        for algo in converted_algo_list:
            filename = filename + algo['algo_id'] + '_'

        filename = filename + 'algo_list.json'
        str_algo_list = json.dumps(converted_algo_list, ensure_ascii=False, indent=2).encode('utf-8')
        ui.download(str_algo_list, filename)

    def _build(self):
        with ui.row().classes('w-full items-end justify-items-end'):
            ui.label('产线定位检测').classes('text-h6 font-bold')
        with ui.row().classes('w-full content-start'):
            with ui.column().classes('w-64 content-start'):
                ui.label('图片上传').classes('font-bold')
                ui.upload(label='产线照片上传', on_upload=self.handle_fileuploader_pic_upload,
                          auto_upload=True).classes(
                    'max-w-full')

        ui.separator()
        with ui.row().classes('w-full items-end justify-items-end'):
            ui.select(options=self.opencv_algo_wrapper.get_select_algo_list(),
                      label='已选算法列表').classes('w-64').on_value_change(self.handle_selector_algo_selected)
            ui.button("添加", on_click=self.handle_btn_add_to_algo_list)
            ui.button("删除选中", on_click=self.handle_btn_remove_from_algo_list)
            ui.button("导出算法清单", on_click=self.export_algo_list)
            ui.button("导入算法清单", on_click=lambda: ui.upload(json.dumps(self.added_algo_item_list), 'hello.json'))
            ui.button('执行列表算法', on_click=self.handle_btn_apply_algo_of_list)

        with ui.row().classes('w-full items-start justify-items-end'):
            with ui.column().classes('w-2/5 h-full items-start'):
                self.algo_aggrid = ui.aggrid({
                    "columnDefs": [
                        {'headerName': '算法UID', 'field': 'algo_id'},
                        {'headerName': '算法标识', 'field': 'algo_alias'},
                        {'headerName': '算法名称', 'field': 'algo_name'},
                        {'headerName': '算法参数', 'field': 'algo_params'}
                    ],
                    "rowData": self.added_algo_item_list,
                    'rowSelection': 'single',
                }).on('cellClicked', self.handle_row_selected)

            with ui.column().classes('w-1/2 min-h-96 items-start') as self.algo_params_panel:
                self.build_algo_params_panel()

            async def get_data() -> None:
                data = await self.json_editor.run_editor_method('get')
                ui.notify(data)
                ui.button('Get Data', on_click=get_data)

        with ui.row().classes('w-full items-end justify-items-end'):
            with ui.column().classes('w-96 content-start'):
                src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
                ui.label('原始图像').classes('font-bold')
                self.src_iimage = ui.interactive_image(src, on_mouse=self.handle_mouse_iimage_init,
                                                       events=['mousedown', 'mouseup'],
                                                       cross='red')
            with ui.column().classes('w-96 content-start'):
                ui.label('处理后图像').classes('font-bold')
                self.dst_img_processed = ui.interactive_image(src, on_mouse=self.handle_mouse_ii_processed,
                                                              events=['mousedown', 'mouseup'],
                                                              cross='red')
